/**
 * 
 */
package ca.uwindsor.cs.deepweb.estimation.experiment;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;

import ca.uwindsor.cs.deepweb.estimation.DataCollector;
import ca.uwindsor.cs.deepweb.estimation.DataCollectorWithLimitationRandom;
import ca.uwindsor.cs.deepweb.estimation.DocumentFrequencyAnalyzer;
import ca.uwindsor.cs.deepweb.estimation.FilesContentIndexer;
import ca.uwindsor.cs.deepweb.estimation.RandomWordSelector;

/**
 * Manage the entire query work from the index to generate output files
 * 
 * @author Liang Jie
 * 
 */
public class QueryRunner {

	/**
	 * The dictionary to restore Lucene index file
	 */
	protected String indexDir;

	/**
	 * The instance of RandomWordSelector to randomly pick a word from
	 * dictionary specified
	 * 
	 * @see #dictFile
	 */
	protected RandomWordSelector selector;

	/**
	 * The dictionary to restore query result
	 */
	protected String resultDir;

	/**
	 * The dictionary file
	 */
	protected String dictFile;

	/**
	 * The length of the character needed
	 */
	protected int wordlength;

	/**
	 * The query step
	 */
	protected int querystep;

	protected boolean isLimit;

	protected int stepsize;

	/**
	 * The DataCollector to process each returned query
	 */
	protected DataCollector datacollector;

	/**
	 * The QueryParser
	 */
	protected QueryParser queryparser;

	/**
	 * The IndexSearcher
	 */
	protected IndexSearcher searcher;
	
	/**
	 * The size of corpus
	 */
	protected int corpus_size;
	
	protected final String[] termslist;

	/**
	 * Return random documents
	 */
	public static short Return_Random = 1;
	
	/**
	 * Return top documents
	 */
	public static short Return_Top = 2;
	
	/**
	 * Run a query
	 * 
	 * @param indexdir
	 *            The dictionary to restore Lucene index file
	 * @param corpussize
	 * The size of corpus
	 * @param list 
	 * @param dictfile
	 *            The dictionary file
	 * @param length
	 *            The length of the character needed
	 * @param result
	 *            The dictionary to restore query result
	 * @throws IOException 
	 */
	public QueryRunner(String indexdir, int corpussize,  String[] list, String dictfile, String length,
			String result) throws IOException {
		indexDir = indexdir;
		corpus_size = corpussize;
		this.termslist = list;
		dictFile = dictfile;
		wordlength = Integer.parseInt(length);
		resultDir = result;
		initialize();
	}
	

	/**
	 * Initialize the objects
	 * @throws IOException 
	 */
	protected void initialize() throws IOException {
		selector = new RandomWordSelector(dictFile, "UTF-8");
		queryparser = new QueryParser(FilesContentIndexer.FIELD_CONTENT,
				new StandardAnalyzer());
		
	}

	/**
	 * Run query
	 * 
	 * @param step
	 *            the query steps
	 * @throws Exception
	 *             All exceptions
	 */
	public void goQuery(int step) throws Exception {
		if (isLimit) {
			datacollector = new DataCollectorWithLimitationRandom(
					FilesContentIndexer.FIELD_ID, stepsize);
		} else {
			datacollector = new DataCollector(FilesContentIndexer.FIELD_ID);
		}
		
		querystep = step;
		if (!isStepSmallerThenSize()) {
			System.out.println("The dictionary has "
					+ selector.getDictionarySize() + " words!");
			System.out
					.println("The step should be less then the amount of words in the directory!");
			System.exit(-2);
		} else {
			// do the preparation
			searcher = new IndexSearcher(indexDir);
			Hits hits = null;
			Query query = null;

			StringBuffer stringbuffer = new StringBuffer();

			stringbuffer.append("The dictionary has "
					+ selector.getDictionarySize() + " words" + "\n");

			for (int k = 1; k <= querystep; k++) {
				stringbuffer.append("k=" + k + "\t");

				String string_randomword = getRandomWord();
				stringbuffer.append(string_randomword + "\t");

				query = queryparser.parse(string_randomword);
				hits = searcher.search(query);
				datacollector.add(hits);
				
				long unique = datacollector.getUnique();
		        long accumulcativetotal = datacollector.getTotalItems();
		        //long stepreturn = datacollector.getReturnedItems();
		        double precentagecovered;
		        double OR;
		        //int duplicatedfromlastquery = datacollector.getDuplicatedItemsCompareToLastQuery();
		        long estimatedsize;
		        
		        if(unique == 0){
		            OR = 0;
		        }else{
		            OR = Double.longBitsToDouble(accumulcativetotal)/ Double.longBitsToDouble(unique);
		        }
		        precentagecovered = Double.longBitsToDouble(unique)/Double.longBitsToDouble(corpus_size);
		        
//		        OR_N_B=new Double(Double.longBitsToDouble(unique) / (1-1 / Math.pow(OR, 2.2 / 2)));
		        estimatedsize = Double.doubleToLongBits(Double.longBitsToDouble(unique) / (1-1 / Math.pow(OR, 2.2 / 2)));
		        
		        stringbuffer.append(datacollector.getReturnedItems()+"\t");
		        stringbuffer.append(unique + "\t");
				stringbuffer.append(accumulcativetotal + "\t");
				stringbuffer.append(OR + "\t");
				stringbuffer.append(precentagecovered + "\t");
		        if(OR == 0.0){
		        	stringbuffer.append("-");
		        }else{
		        	stringbuffer.append(estimatedsize);
		        }
//		        result[6] = datacollector.getReturnedItems();
//		        result[7] = duplicatedfromlastquery;
//		        result[8] = datacollector.getMarkedItems();
		        stringbuffer.append("\n");
			}
			searcher.close();

			Calendar calendar = Calendar.getInstance();
			String timestamp = calendar.get(Calendar.YEAR) + ""
					+ calendar.get(Calendar.MONTH) + ""
					+ calendar.get(Calendar.DATE) + ""
					+ calendar.get(Calendar.HOUR) + ""
					+ calendar.get(Calendar.MINUTE) + ""
					+ calendar.get(Calendar.SECOND);
			System.out.println(timestamp);

			StringBuffer resultfile = new StringBuffer();
			resultfile.append(resultDir);
			resultfile.append(java.io.File.separator);
			resultfile.append("wiki");
			resultfile.append(timestamp);
			resultfile.append("_");
			resultfile.append(getDictionaryName());
			resultfile.append("_");
			resultfile.append(querystep);
			resultfile.append(".txt");
			FileOutputStream fp = new FileOutputStream(resultfile.toString());
			fp.write(stringbuffer.toString().getBytes("UTF-8"));
			fp.flush();
			fp.close();
			System.out.println("Result has bee save in "
					+ resultfile.toString());
			
			//save document frequency list
			stringbuffer = new StringBuffer();
			DocumentFrequencyAnalyzer dfa = datacollector.getDocumentFrequencyAnalyzer();
			HashMap<Integer, Integer> map = dfa.getFrequencyMap();
			Set<Entry<Integer, Integer>> s = map.entrySet();
			for(Entry<Integer, Integer> e : s){
					stringbuffer.append(e.getKey());
					stringbuffer.append("\t");
					stringbuffer.append(e.getValue());
					stringbuffer.append("\n");
			}
			StringBuffer frequencyfile = new StringBuffer();
			frequencyfile.append(resultDir);
			frequencyfile.append(java.io.File.separator);
			frequencyfile.append("wiki");
			frequencyfile.append(timestamp);
			frequencyfile.append("_");
			frequencyfile.append(getDictionaryName());
			frequencyfile.append("_");
			frequencyfile.append(querystep);
			frequencyfile.append("_frequency");
			frequencyfile.append(".txt");
			fp = new FileOutputStream(frequencyfile.toString());
			fp.write(stringbuffer.toString().getBytes("UTF-8"));
			fp.flush();
			fp.close();
		}
	}

	/**
	 * Get the name of the Dictionary
	 * 
	 * @return the name of the Dictionary
	 */
	protected String getDictionaryName() {
		int offset1 = dictFile.lastIndexOf(java.io.File.separator);
		if (offset1 == -1) {
			offset1 = 0;
		}
		int offset2 = dictFile.lastIndexOf(".txt");
		if (offset2 == -1) {
			offset2 = dictFile.length() - 1;
		}
		String name = dictFile.substring(offset1 + 1, offset2);
		return name;
	}

	/**
	 * 
	 * @return a random word
	 */
	protected String getRandomWord() {
		String string_randomword = selector.getRandomPhrase();
		while (string_randomword.length() <= wordlength)
			string_randomword = selector.getRandomPhrase();
		return string_randomword;
	}

	/**
	 * Whether the step is smaller the size of words in dictionary
	 * 
	 * @return true if step is smaller the size of words in dictionary
	 */
	protected boolean isStepSmallerThenSize() {
		if (querystep <= selector.getDictionarySize()) {
			return true;
		} else
			return false;
	}

	/**
	 * @return the querystep
	 */
	public int getQueryStep() {
		return querystep;
	}

	/**
	 * @param querystep
	 *            the querystep to set
	 */
	public void setQueryStep(int querystep) {
		this.querystep = querystep;
	}

	public void setStepSize(int limitreturn) {
		if (limitreturn > 0) {
			isLimit = true;
			stepsize = limitreturn;
		}
	}

	public int getStepSize() {
		return stepsize;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
//		 args = new String[7];
//		 args[0] = "d:\\学习资料\\Graduate Study\\Research\\Experiment\\en_wiki_sample_2000";
//		 args[1] = "2000";
//		 args[2] = "d:\\学习资料\\Graduate Study\\Research\\Dictionaries\\webster.txt";
//		 args[3] = "1";
//		 args[4] = "d:\\学习资料\\Graduate Study\\Research\\Result";
//		 args[5] = "100";
//		 args[6] = "10";
//
//		if (args.length != 7) {
//			System.out
//					.println("usage: java -jar QueryRunner.jar LuceneIndexDirectory CorpusSize DictionaryFile min[Letter/Character]Length ResultDirectory Steps StepSize(limitation on query result, -1 means no limitation)");
//		} else {
//			int corpussize = Integer.parseInt(args[1]);
//			QueryRunner query = new QueryRunner(args[0], corpussize, args[2], args[3],
//					args[4]);
//			int step = Integer.parseInt(args[5]);
//			int lim = Integer.parseInt(args[6]);
//			query.setStepSize(lim);
//			try {
//				query.goQuery(step);
//			} catch (Exception e) {
//				e.printStackTrace();
//			}
//		}
	}
}
